//笔锋绘制
class ControlPoint {

    constructor(x, y, width) {
        this.x = x
        this.y = y
        this.width = width
    }

    set(x, y, width) {
        this.x = x
        this.y = y
        this.width = width
    }

    setControlPoint(point) {
        this.x = point.x
        this.y = point.y
        this.width = point.width
    }
}
// 贝塞尔类
class Bezier {
    constructor() {
        //    控制点
        this.mControl = new ControlPoint();
        //    距离
        this.mDestination = new ControlPoint()
        //    下一个需要控制点
        this.mNextControl = new ControlPoint()
        //    资源的点
        this.mSource = new ControlPoint()
    }
    initPoint(last, cur) {
        this.init(last.x, last.y, last.width, cur.x, cur.y, cur.width)
    }
    init(lastX, lastY, lastWidth, x, y, width) {
        //资源点设置 最后的点位资源点
        this.mSource.set(lastX, lastY, lastWidth)
        let xMid = getMid(lastX, x)
        let yMid = getMid(lastY, y)
        let wMid = getMid(lastWidth, width)
        //    距离单为平均点
        this.mDestination.set(xMid, yMid, wMid)
        //   控制点为当前的距离点
        this.mControl.set(getMid(lastX, xMid), getMid(lastY, yMid), getMid(lastWidth, wMid))
        //    下个控制点为当前点
        this.mNextControl.set(x, y, width)
    }
    addNodePoint(cur) {
        this.addNode(cur.x, cur.y, cur.width)
    }

    //替换旧的点 原来的距离点变换为资源点，控制点变为原来的下一个控制点，距离点取原来控制点的和新的的一半 下个控制点为新的点
    addNode(x, y, width) {
        this.mSource.setControlPoint(this.mDestination)
        this.mControl.setControlPoint(this.mNextControl)
        this.mDestination.set(getMid(this.mNextControl.x, x), getMid(this.mNextControl.y, y), getMid(this.mNextControl.width, width))
        this.mNextControl.set(x, y, width);
    }

    //结束时改变点的位置
    penEnd() {
        this.mSource.setControlPoint(this.mDestination)
        this.mControl.set(getMid(this.mNextControl.x, this.mSource.x), getMid(this.mNextControl.y, this.mSource.y), getMid(this.mNextControl.width, this.mSource.width))
        this.mDestination.setControlPoint(this.mNextControl)
    }

    //    获取点的信息
    getPoint(t) {
        let point = new ControlPoint()
        point.set(this.getX(t), this.getY(t), this.getW(t))
        return point
    }

    //三阶曲线控制点
    getValue(p0, p1, p2, t) {
        let a = p2 - 2 * p1 + p0
        let b = 2 * (p1 - p0)
        return a * t * t + b * t + p0
    }

    getX(t) {
        return this.getValue(this.mSource.x, this.mControl.x, this.mDestination.x, t)
    }

    getY(t) {
        return this.getValue(this.mSource.y, this.mControl.y, this.mDestination.y, t)
    }

    getW(t) {
        return getWidth(this.mSource.width, this.mDestination.width, t)
    }
}
// 计算压力值
const calculatePressure = force => force >= 0 && force <= 20 ? 40 : force > 20 && force <= 40 ? 60 : force > 40 && force <= 60 ? 80 : force > 60 && force <= 90 ? 100 : force > 90 && force <= 150 ? 120 : 130;
// 获取中间值
const getMid = (x, y) => (x + y) / 2
// 计算宽度
const getWidth = (w0, w1, t) => w0 + (w1 - w0) * t
// 当前点
let curPoint = null
// 上一个点
let mLastPoint = null
// 计算出来的线段宽度
let mLastWidth = null
// 贝塞尔类实例
let mBezier = new Bezier()
// 笔画的第一点
let mFirstPoint = null
// 点击数
let pointNum = 0
//转换参数
const transFormScale = 80
// 每笔的数据
let nowList = []
// 上一压力值
let lastForce = null
// down点（笔锋）
const penStrokeDown = (x, y, force, width) => {
    let pressure = calculatePressure(force)
    mLastWidth = pressure / transFormScale * width
    pointNum = 1
    // 记录down点信息
    curPoint = new ControlPoint(x, y, mLastWidth)
    mLastPoint = new ControlPoint(x, y, mLastWidth)
    mFirstPoint = new ControlPoint(x, y, mLastWidth)
    nowList = []
    nowList.push(curPoint)
    lastForce = force
}
// move点方法（笔锋）
const penStrokeMove = (x, y, force, penWidth) => {
    let pressure = calculatePressure(force)
    let pressureCheck = forceCreck(lastForce, pressure)
    lastForce = pressureCheck
    let curWidth = pressureCheck / transFormScale * penWidth
    curPoint = new ControlPoint(x, y, curWidth)
    let curDis = Math.hypot(curPoint.x - mLastPoint.x, curPoint.y - mLastPoint.y)
    if (pointNum === 1) {
        pointNum++;
        mBezier.initPoint(mLastPoint, curPoint);
    } else {
        mBezier.addNodePoint(curPoint);
    }
    mLastWidth = curWidth;
    doMove(curDis);
    mLastWidth = new ControlPoint(curPoint.x, curPoint.y, curPoint.width)
}
// up点方法（笔锋）
const penStrokeUp = (x, y, force, context, penWidth, penColor) => {
    if (nowList.length === 0) {
        return
    }
    curPoint = new ControlPoint(x, y, 0)
    let deltaX = curPoint.x - mLastPoint.x
    let deltaY = curPoint.y - mLastPoint.y
    let curDis = Math.hypot(deltaX, deltaY)
    mBezier.addNodePoint(curPoint)
    let steps = 1 + Math.floor((curDis / 10))
    let step = 1 / steps
    for (let t = 0; t < 1; t += step) {
        let point = mBezier.getPoint(t)
        nowList.push(point)
    }
    mBezier.penEnd()
    for (let t = 0; t < 1; t += step) {
        let point = mBezier.getPoint(t)
        nowList.push(point)
    }
    draws(context, penWidth, penColor);
    nowList = []
}

function doMove(curDis) {
    let steps = 1 + Math.floor((curDis / 10));
    let step = 1 / steps
    for (let t = 0; t < 1; t += step) {
        let Point = mBezier.getPoint(t)
        nowList.push(Point)
    }
}

function draws(context, penWidth, penColor) {
    doPreDraw(context, penWidth, penColor);
}

function doPreDraw(context, penWidth, penColor) {
    let curPoint = nowList[0]
    let length = nowList.length
    for (let i = 1; i < length; i++) {
        drawPoint(curPoint, nowList[i], context, penWidth, penColor)
        curPoint = nowList[i]
    }
}

function drawPoint(curPoint, point, context, penWidth, penColor) {
    // 相同点不绘制
    if (curPoint.x === point.x && curPoint.y === point.y) {
        return
    }
    drawLine(curPoint.x, curPoint.y, curPoint.width, point.x, point.y, point.width, context, penWidth, penColor);
}
// 绘制方法
function drawLine(x0, y0, w0, x1, y1, w1, context, penWidth, penColor) {
    let curDis = Math.hypot(x1 - x0, y1 - y0)
    let step = 1
    if (penWidth <= 6) {
        step = 1 + Math.floor((curDis))
    } else if (penWidth > 60) {
        step = 1 + Math.floor((curDis / 4))
    } else {
        step = 1 + Math.floor((curDis / 3))
    }
    let deltaX = (x1 - x0) / step
    let deltaY = (y1 - y0) / step
    let deltaW = (w1 - w0) / step
    let x = x0
    let y = y0
    let w = w0
    for (let i = 0; i < step; i++) {
        let left = x + w / 2
        let top = y + w / 2
        let right = x - w / 2
        let bottom = y - w / 2
        let midPointX = (left + right) / 2
        let midPointY = (top + bottom) / 2
        let xRadius = Math.abs((left - right) / 2)
        let yRadius = Math.abs((top - bottom) / 2)
        context.setLineDash([])
        context.beginPath();
        context.ellipse(midPointX * 0.97, midPointY * 0.976, xRadius, yRadius, 0, 0, Math.PI * 2);
        context.strokeStyle = penColor;
        context.stroke();
        context.closePath();
        context.fill();
        x += deltaX
        y += deltaY
        w += deltaW
    }
}
// 压力值前后差距过大补正
const forceCreck = (lastForce, curForce) => {
    if ((curForce - lastForce) > 35) {
        return (lastForce + curForce) / 2
    }
    return curForce
}
/**
 * @name 笔锋绘制
 * @param {number} type 点类型
 * @param {number} x 点x坐标
 * @param {number} y 点y坐标
 * @param {number} force 点压力值
 * @param {number} context canvas上下文
 * @param {number} penWidth 线宽
 * @param {string} penColor 线色
 */
export default function(type, x, y, force, context, penWidth, penColor) {
    switch (type) {
        case 0:
            penStrokeDown(x, y, force, penWidth)
            break;
        case 1:
            penStrokeMove(x, y, force, penWidth)
            break;
        case 2:
            penStrokeUp(x, y, force, context, penWidth, penColor)
            break;
    }
}